/*
 *  nss-get-cert - Extract a certificate from the NSS database.
 *
 *  This file initially created by Google, Inc.
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License version 2 as
 *  published by the Free Software Foundation.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */


#define _GNU_SOURCE /* for asprintf() */
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>

#include "base64.h" /* for BTOA_DataToAscii */
#include "nspr.h"
#include "nss.h"
#include "cert.h"

typedef enum {
        CERT_FORMAT_PEM,
        CERT_FORMAT_DER
} cert_format;

const char cert_header[] = "-----BEGIN CERTIFICATE-----\n";
const char cert_trailer[] = "\n-----END CERTIFICATE-----\n";

static int write_certificate(CERTCertificate *cert, FILE *outfile, 
                             cert_format format)
{
        if (format == CERT_FORMAT_DER) {
                if (1 != fwrite(cert->derCert.data, cert->derCert.len, 1,
                                outfile)) {
                        syslog(LOG_ERR, "Couldn't write certificate\n");
                        return -EIO;
                }
        } else if (format == CERT_FORMAT_PEM) {
                char *pem_data = BTOA_DataToAscii(cert->derCert.data,
                                                  cert->derCert.len);
                if (fputs(cert_header, outfile) < 0 ||
                    fputs(pem_data, outfile) < 0 ||
                    fputs(cert_trailer, outfile) < 0) {
                        free(pem_data);
                        syslog(LOG_ERR, "Couldn't write certificate\n");
                        return -EIO;
                }
                free(pem_data);
        } else {
                syslog(LOG_ERR, "Unexpected certificate format\n");
                return -EIO;
        }
        return 0;
}


int main(int argc, char *argv[])
{
        SECStatus secrv;
        CERTCertDBHandle *handle;
        const char *homedir, *certname, *format_str;
        int rv = EXIT_FAILURE;
        char *certdir = NULL, *outfilename = NULL;
        CERTCertificate *cert = NULL;
        FILE *outfile = NULL;
        cert_format format = CERT_FORMAT_PEM;

        openlog("nss-get-cert", LOG_PERROR, LOG_USER);
        if (argc < 3) {
                syslog(LOG_ERR,
                       "%s: Must pass certificate nickname and format\n",
                        argv[0]);
                return EXIT_FAILURE;
        }
        certname = argv[1];
        format_str = argv[2];

	if (!strcmp(format_str, "der")) {
		format = CERT_FORMAT_DER;
	} else if (!strcmp(format_str, "pem")) {
		format = CERT_FORMAT_PEM;
        } else {
		syslog(LOG_ERR,
		       "%s: Invalid format parameter: %s\n", format);
                return EXIT_FAILURE;
        }

        if (argc > 4) {
                syslog(LOG_ERR, "%s: Too many arguments\n", argv[0]);
                return EXIT_FAILURE;
        }
        if (argc == 4)
                outfilename = argv[3];

        homedir = getenv("HOME");
        if (!homedir)
                homedir = "/home/chronos/user";

        if (-1 == asprintf(&certdir, "sql:%s/.pki/nssdb", homedir)) {
                syslog(LOG_ERR, "%s: asprintf failure\n", argv[0]);
                return EXIT_FAILURE;
        }

        secrv = NSS_Init(certdir);
        if (secrv != SECSuccess) {
                char buffer[513];
                int errlen = PR_GetErrorTextLength();
                if (errlen > 0 && errlen < sizeof (buffer)) {
                        PR_GetErrorText(buffer);
                        syslog(LOG_ERR, "%s: %s\n", argv[0], buffer);
                } else
                        syslog(LOG_ERR, "%s: Unprintable error\n",
                                argv[0]);
                return EXIT_FAILURE;
        }

        handle = CERT_GetDefaultCertDB();
        cert = CERT_FindCertByNickname(handle, certname);
        if (!cert) {
                syslog(LOG_ERR, "%s: Couldn't find certificate '%s'\n",
                        argv[0], certname);
                goto out;
        }

        if (outfilename) {
                outfile = fopen(outfilename, "wb");
                if (!outfile) {
                        syslog(LOG_ERR, "%s: Couldn't open output file '%s':"
                                " %s\n", argv[0], outfilename,
                                strerror(errno));
                        rv = EXIT_FAILURE;
                        goto out;
                }
        } else
                outfile = stdout;

        if (write_certificate(cert, outfile, format) < 0)
                goto out;

        rv = EXIT_SUCCESS;

out:
        free(certdir);
        if (cert)
                CERT_DestroyCertificate(cert);
        NSS_Shutdown();
        if (outfile) {
                fclose(outfile);
                chmod(outfilename, 0644);
        }

        return rv;
}
